package br.mikhas.util.jdbc;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.Date;

/**
 * Utility class to help dealing with Jdbc
 * 
 * @author Mikhail Domanoski
 * 
 */
public final class JdbcUtils {

	/**
	 * Utility class
	 */
	private JdbcUtils() {
	}

	/**
	 * Closes the given JDBC connection.
	 * 
	 * @param conn
	 *            The {@link Connection} to close
	 */
	public static void close(Connection conn) {
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
			} catch (Throwable t) {
			}
		}
	}

	/**
	 * Closes the given JDBC statement
	 * 
	 * @param stmt
	 *            The {@link Statement} to be closed
	 */
	public static void close(Statement stmt) {
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
			} catch (Throwable t) {
			}
		}
	}

	/**
	 * Closes the given JDBC ResultSet
	 * 
	 * @param rs
	 *            The {@link ResultSet} to be closed
	 */
	public static void close(ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
			} catch (Throwable t) {
			}
		}
	}

	/**
	 * Populates an statement parameter using the proper method
	 * 
	 * @param ps
	 *            The statement to be populed
	 * @param index
	 *            The index of the parameter
	 * @param parameter
	 *            The parameter to be setted
	 */
	public static void setStatementValue(PreparedStatement ps, int index,
			Object value) {
		try {

			if (value instanceof java.sql.Date) {
				ps.setDate(index, (java.sql.Date) value);

			} else if (value instanceof java.sql.Timestamp) {
				ps.setTimestamp(index, (java.sql.Timestamp) value);

			} else if (value instanceof java.sql.Time) {
				ps.setTime(index, (java.sql.Time) value);
			} else if (value instanceof Integer) {

				Integer i = (Integer) value;
				ps.setInt(index, i.intValue());

			} else if (value instanceof Long) {

				Long l = (Long) value;
				ps.setLong(index, l.longValue());

			} else if (value instanceof Double) {

				Double d = (Double) value;
				ps.setDouble(index, d.doubleValue());

			} else if (value instanceof Short) {

				Short s = (Short) value;
				ps.setShort(index, s.shortValue());

			} else if (value instanceof Float) {

				Float f = (Float) value;
				ps.setFloat(index, f.floatValue());

			} else if (value instanceof Boolean) {

				Boolean b = (Boolean) value;
				ps.setBoolean(index, b.booleanValue());

			} else if (value instanceof BigDecimal) {

				BigDecimal bd = (BigDecimal) value;
				ps.setBigDecimal(index, bd);

			} else if (value instanceof Byte) {

				Byte b = (Byte) value;
				ps.setByte(index, b.byteValue());

			} else if (value instanceof Date) {
				Timestamp t = new Timestamp(((Date) value).getTime());
				ps.setTimestamp(index, t);

			} else if (value instanceof String || value instanceof Character) {
				ps.setString(index, String.valueOf(value));

			} else {
				ps.setObject(index, value);
			}

		} catch (SQLException e) {
			throw new JdbcTypeException(e);
		}
	}

	/**
	 * Extract a JDBC column value from a ResultSet using the given type to
	 * parse it.
	 * 
	 * @param rs
	 *            The result set holding the data
	 * @param index
	 *            The index of the column which the data will be extracted from
	 * @param requiredType
	 *            The user requisred type
	 * @return The value object
	 */
	public static Object getResultSetValue(ResultSet rs, int index,
			Class<?> requiredType) {

		try {

			if (requiredType == String.class) {
				return rs.getString(index);
			} else if (requiredType == char.class
					|| requiredType == Character.class) {

				char chr = rs.getString(index).charAt(0);
				return Character.valueOf(chr);

			} else if (requiredType == int.class
					|| requiredType == Integer.class) {
				return Integer.valueOf(rs.getInt(index));

			} else if (requiredType == boolean.class
					|| requiredType == Boolean.class) {
				return Boolean.valueOf(rs.getBoolean(index));

			} else if (requiredType == long.class || requiredType == Long.class) {
				return Long.valueOf(rs.getLong(index));

			} else if (requiredType == double.class
					|| requiredType == Double.class
					|| Number.class.equals(requiredType)) {
				return Double.valueOf(rs.getDouble(index));

			} else if (requiredType == java.sql.Date.class) {
				return rs.getDate(index);

			} else if (requiredType == java.sql.Timestamp.class
					|| requiredType == java.util.Date.class) {
				return rs.getTimestamp(index);

			} else if (java.sql.Time.class == requiredType) {
				return rs.getTime(index);

			} else if (BigDecimal.class == requiredType) {
				return rs.getBigDecimal(index);

			} else if (byte[].class == requiredType) {
				return rs.getBytes(index);

			} else {
				rs.getObject(index);
			}

			throw new JdbcTypeException("Unmapped type: " + requiredType);
		} catch (SQLException e) {
			throw new JdbcTypeException(
					"Could not get resultset value at column #" + index, e);
		}
	}
}
